//------------------------------------------------------------
//  Filename: tcdm_lint_mux.sv
//   
//  Author  : wlduan@gmail.com
//  Revise  : 2019-10-17 12:08
//  Description: 
//   
//  Copyright (C) 2014, YRBD, Inc. 					      
//  All Rights Reserved.                                       
//-------------------------------------------------------------
//
`timescale 1ns/1ps
 
module tcdm_lint_demux #(
    parameter ADDR_WIDTH = 32,
    parameter DATA_WIDTH = 32,
    parameter BE_WIDTH   = DATA_WIDTH/8,
    parameter N_PORTS    = 2,
)
( 
    input logic    clk,  
    input logic    rst_n,  
         
    input logic [ADDR_WIDTH-1:0] START_ADDR[N_PORTS],
    input logic [ADDR_WIDTH-1:0] END_ADDR[N_PORTS],

    LINT_IF.Slave  lint_slave,
    LINT_IF.Master lint_master[N_PORTS]
);      
//-------------------------------------------------------------
logic [N_PORTS-1:0] active_i;
logic [N_PORTS-1:0] active_q;
logic [N_PORTS-1:0] active_int;
//-------------------------------------------------------------
always_ff @(posedge clk,negedge rst_n) begin
    if(rst_n == 0)begin 
        active_q <= 'b0; 
    end 
    else begin 
        active_q <= active_int; 
    end 
end 
//-------------------------------------------------------------
enum logic[7:0] {IDLE,WAIT_GNT,WAIT_VLD} cs,ns;
//-------------------------------------------------------------
always_ff @(posedge clk,negedge rst_n) begin
    if(rst_n == 0)begin 
        cs <= IDLE; 
    end 
    else begin 
        cs <= ns; 
    end 
end 
//-------------------------------------------------------------
always_comb begin
    ns = cs;
    active_int = active_q;
    case(cs)
        IDLE: begin
            if(lint_slave.data_req) begin
                active_int = active_i;
                if(lint_slave.data_gnt) begin
                    ns = WAIT_VLD;
                end
                else begin
                    ns = WAIT_GNT;
                end
            end
        end
        WAIT_GNT: begin
            if(lint_slave.data_gnt) begin 
                ns = WAIT_VLD;
            end
        end
        WAIT_VLD: begin
            if(lint_slave.data_r_valid) begin
                if(lint_slave.data_req) begin
                    active_int = active_i;
                    if(lint_slave.data_gnt) begin
                        ns = WAIT_VLD;
                    end
                    else begin
                        ns = WAIT_GNT;
                    end
                end 
                else begin
                    ns = IDLE;
                end  
            end
        end
    endcase
end
//-------------------------------------------------------------
logic [N_PORTS -1:0]           ports_gnt;
logic [N_PORTS -1:0]           ports_rdy;
logic [N_PORTS -1:0]           ports_r_opc;
logic [N_PORTS -1:0]           ports_r_valid;
logic [N_PORTS*DATA_WIDTH-1:0] ports_r_data;
//-------------------------------------------------------------
generate 
    genvar i;
    for(i=0;i<N_PORTS;i++) begin
        assign active_i[i]  = ((lint_slave.data_addr >= START_ADDR[i])&&(lint_slave.data_addr < END_ADDR[i])); 
        assign ports_rdy[i] = ((((cs == IDLE)||(cs == WAIT_VLD))&active_i[i])||((cs == WAIT_GNT)&active_q[i]));
        always_comb begin
            lint_master[i].data_addr  = 'b0;
            lint_master[i].data_wdata = 'b0;
            lint_master[i].data_we    = 'b0;
            lint_master[i].data_be    = 'b0;
            if(active_q[i]) begin
                lint_master[i].data_addr  = lint_slave.data_addr  ; 
                lint_master[i].data_wdata = lint_slave.data_wdata ; 
                lint_master[i].data_we    = lint_slave.data_we    ; 
                lint_master[i].data_be    = lint_slave.data_be    ;
            end
        end        
        assign ports_gnt[i]                            = lint_master[i].data_gnt;
        assign ports_r_opc[i]                          = lint_master[i].data_r_valid;
        assign ports_r_valid[i]                        = lint_master[i].data_r_opc;
        assign ports_r_data[DATA_WIDTH*i +:DATA_WIDTH] = lint_master[i].data_r_rdata;
    end
endgenerate
//-------------------------------------------------------------
assign lint_slave.data_gnt = |(ports_gnt&ports_rdy);
//-------------------------------------------------------------
always_comb begin
    lint_slave.data_r_rdata = 'b0;
    lint_slave.data_r_valid = 'b0;
    lint_slave.data_r_opc   = 'b0;
    for(reg[7:0] i=0;i<N_PORTS;i++) begin
        if((cs == WAIT_VLD)&active_q[i]) begin
            lint_slave.data_r_rdata = ports_r_data[DATA_WIDTH*i +:DATA_WIDTH];
            lint_slave.data_r_valid = ports_r_valid[i];
            lint_slave.data_r_opc   = ports_r_opc[i];
        end
    end
end 
//-------------------------------------------------------------
      
endmodule
